package org.krivtsun.scanlab.batch;

import org.krivtsun.scanlab.utils.FileTypeFilters;
import org.krivtsun.scanlab.utils.Functions;

import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * todo Dynamic change minimum list width
 * todo Decorate large images loading (progress bar while loading multiple pages) 
 * next Change pages order in batch
 * next Batch storing in separate folder like in FR + xml format for batch description
 * next Implement pages list view in two modes: one like in FR and second like in DjVu Solo
 *  
 * @author Andrew Krivtsun
 */
public class ImageBatch {

    private BatchModel model = new BatchModel();
    private JList view = new JList(model);

    private JLabel statusBarItem = new JLabel();

    private Action newBatchAction,
                   appendPagesAction, deletePagesAction;

    public ImageBatch() {
        model.addListDataListener(new ModelDataListener());

        view.setLayoutOrientation(JList.HORIZONTAL_WRAP);
        view.setVisibleRowCount(-1);
        view.setCellRenderer(new PageCellRenderer());

//        view.addComponentListener(new ComponentAdapter() {
//            public void componentResized(ComponentEvent e) {
//                System.out.println("!!! getSize()="+view.getSize());
//                System.out.println("!!! getVisibleRowCount()="+view.getVisibleRowCount());
//                System.out.println("!!! sp.getViewport().getViewSize()="+sp.getViewport().getSize());
//            }
//        });
        view.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                updateActions();
                updateStatusBar();
            }
        });

        updateActions();
        updateStatusBar();
    }

    public Action getNewBatchAction() {
        if (newBatchAction == null)
            newBatchAction = createNewBatchAction();
        return newBatchAction;
    }

    public Action getAppendPagesAction() {
        if (appendPagesAction == null)
            appendPagesAction = createAppendPagesAction();
        return appendPagesAction;
    }

    public Action getDeletePagesAction() {
        if (deletePagesAction == null)
            deletePagesAction = createDeletePagesAction();
        return deletePagesAction;
    }

    public void addBatchSelectionListener(ListSelectionListener listener) {
        view.addListSelectionListener(listener);
    }

    public void removeBatchSelectionListener(ListSelectionListener listener) {
        view.removeListSelectionListener(listener);
    }

//    private void addImage(File file) {
//        model.addImage(file);
//        view.setSelectedIndex(model.getSize()-1);
//    }

    public RenderedImage getSelectedImage() {
        int index = view.getLeadSelectionIndex();
        return -1 < index && index < model.getSize()
                ? ((PageEntry)model.getElementAt(index)).getImage()
                : null;
    }

            JScrollPane sp = new JScrollPane(view);
    public JComponent createViewComponent() {
        sp.setBorder(BorderFactory.createTitledBorder("Batch"));

        Insets instes = sp.getBorder().getBorderInsets(sp);
        int width = instes.left + PageCellRenderer.getCellWidth() + UIManager.getInt("ScrollBar.width") + instes.right;
//        System.out.println(width);
        Dimension dim = new Dimension(width, sp.getPreferredSize().height);
        sp.setMinimumSize (dim);
        sp.setPreferredSize(dim);
        sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

        return sp;
    }

    public JComponent getStatusBarItem() {
        return statusBarItem;
    }

    private JFileChooser createFileChooser(String title) {

        JFileChooser chooser = new JFileChooser(System.getProperty("user.dir"));
        chooser.setDialogTitle(title);
        chooser.setMultiSelectionEnabled(true);

        for (FileFilter ff : FileTypeFilters.getFileFilters()) {
            chooser.addChoosableFileFilter(ff);
        }
        chooser.setAcceptAllFileFilterUsed(true);
        chooser.setFileFilter(chooser.getChoosableFileFilters()[0]);  // make first file filter active

        //chooser.setAccessory(new JLabel("setAccessory"));        
        return chooser;
    }

    private void updateActions() {
        boolean hasData = model.getSize() > 0;
        getNewBatchAction().setEnabled(hasData);
        boolean hasSelectedItems = view.getSelectedIndices().length > 0;
        getDeletePagesAction().setEnabled(hasSelectedItems);
    }

    private void updateStatusBar() {
//            updateActions();
        if (model.getSize() == 0)
            statusBarItem.setText("The batch is empty");
        else
            statusBarItem.setText("Page "+(view.getLeadSelectionIndex()+1)+" of "+model.getSize()+" pages");
    }

    private Action createAppendPagesAction() {
        Action action = new AbstractAction("Append Page(s)...") {
            public void actionPerformed(ActionEvent e) {

                JFileChooser fChooser = createFileChooser("Append Pages");
                if (fChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
                    loadImages(fChooser.getSelectedFiles());
                }
            }
        };
        action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl O"));
        action.putValue(Action.MNEMONIC_KEY, new Integer('O'));
        action.putValue(Action.SMALL_ICON, Functions.loadIcon("openImage"));
        return action;
    }

    private void loadImages(final File[] files) {
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < files.length; i++) {
                    final File file = files[i];
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            model.addImage(file);
                            //view.repaint();
                        }
                    });
                }
//                SwingUtilities.invokeLater(new Runnable() {
//                    public void run() {
//                        view.setSelectedIndex(model.getSize()-1);
//                    }
//                });
            }
        }).start();
    }

    private Action createNewBatchAction() {
        Action action = new AbstractAction("New Batch") {
            public void actionPerformed(ActionEvent e) {
                //view.getSelectionModel().clearSelection();
                model.clear();
            }
        };
        action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl N"));
        action.putValue(Action.MNEMONIC_KEY, new Integer('N'));
        action.putValue(Action.SMALL_ICON, Functions.loadIcon("newBatch"));
        return action;
    }

    private Action createDeletePagesAction() {
        Action action = new AbstractAction("Delete Page(s)") {
            public void actionPerformed(ActionEvent e) {
                int[] indices = view.getSelectedIndices();
                //view.clearSelection();
                for (int i = indices.length-1; i >= 0; i--) {
                    model.deleteImageAt(indices[i]);
                }
            }
        };
        action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl DELETE"));
        action.putValue(Action.MNEMONIC_KEY, new Integer('D'));
        return action;
    }

    private static class BatchModel extends AbstractListModel {

        private final List<PageEntry> images = new ArrayList<PageEntry>();

        void addImage(File file) {
            images.add(new PageEntry(file));
            fireIntervalAdded(this, images.size()-1, images.size()-1);
        }

        void deleteImageAt(int index) {
            images.remove(index);
            fireIntervalRemoved(this, index, index);
        }

        void clear() {
            int lastIndex = images.size()-1;
            images.clear();
            fireIntervalRemoved(this, 0, lastIndex);
        }

        public int getSize() {
            return images.size();
        }

        public Object getElementAt(int index) {
            return images.get(index);
        }
    }

    private class ModelDataListener implements ListDataListener {

        public void intervalAdded(ListDataEvent e) {
            updateStatusBar();
        }

        public void intervalRemoved(ListDataEvent e) {
            updateStatusBar();
        }

        public void contentsChanged(ListDataEvent e) {

        }
    }
}
